// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_H_

#include <stdint.h>

#include <memory>

#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/service/display/output_surface.h"
#include "components/viz/service/display_embedder/buffer_queue.h"
#include "components/viz/service/display_embedder/gl_output_surface.h"
#include "components/viz/service/display_embedder/viz_process_context_provider.h"
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/swap_result.h"
#include "ui/gl/gl_surface.h"

namespace viz {

// An OutputSurface implementation that directly draws and swap to a GL
// "buffer_queue" surface (aka one backed by a buffer managed explicitly).
class VIZ_SERVICE_EXPORT GLOutputSurfaceBufferQueue
    : public GLOutputSurface,
      public BufferQueue::SyncTokenProvider {
 public:
  GLOutputSurfaceBufferQueue(
      scoped_refptr<VizProcessContextProvider> context_provider,
      gpu::SurfaceHandle surface_handle,
      std::unique_ptr<BufferQueue> buffer_queue);

  GLOutputSurfaceBufferQueue(const GLOutputSurfaceBufferQueue&) = delete;
  GLOutputSurfaceBufferQueue& operator=(const GLOutputSurfaceBufferQueue&) =
      delete;

  ~GLOutputSurfaceBufferQueue() override;

  // BufferQueue::SyncTokenProvider implementation.
  gpu::SyncToken GenSyncToken() override;

 protected:
  // OutputSurface implementation.
  void SetDisplayTransformHint(gfx::OverlayTransform transform) override;
  gfx::OverlayTransform GetDisplayTransform() override;
  void Reshape(const gfx::Size& size,
               float device_scale_factor,
               const gfx::ColorSpace& color_space,
               gfx::BufferFormat format,
               bool use_stencil) override;

 private:
  FRIEND_TEST_ALL_PREFIXES(GLOutputSurfaceBufferQueueTest, HandleSwapNAK);

  // OutputSurface implementation.
  void BindFramebuffer() override;
  void SwapBuffers(OutputSurfaceFrame frame) override;
  gfx::Rect GetCurrentFramebufferDamage() const override;
  uint32_t GetFramebufferCopyTextureFormat() override;
  bool IsDisplayedAsOverlayPlane() const override;
  unsigned GetOverlayTextureId() const override;
  gpu::Mailbox GetOverlayMailbox() const override;

  // GLOutputSurface:
  void DidReceiveSwapBuffersAck(const gfx::SwapResponse& response,
                                gfx::GpuFenceHandle release_fence) override;

  std::unique_ptr<BufferQueue> buffer_queue_;

  // |buffer_queue_textures_| caches the textures generated by consuming the
  // SharedImage mailboxes from the |buffer_queue_| so that we don't have to
  // generate a new texture every time a shared image is re-used.
  base::flat_map<gpu::Mailbox, unsigned> buffer_queue_textures_;

  // |current_texture_| is the texture currently being drawn to. It's one of
  // |buffer_queue_textures_| or 0 if the client is not currently drawing (i.e.,
  // we're not currently in between a BindFramebuffer()/SwapBuffers() pair).
  // |last_bound_texture_| is the texture that was last bound to |fbo_|. It's
  // also one of |buffer_queue_textures_| or 0 if no texture has been bound to
  // |fbo_| or all the buffers in the buffer queue have been freed.
  // |last_bound_mailbox_| is the mailbox corresponding to
  // |last_bound_texture_|.
  //
  // TODO(andrescj): use an RAII pattern to scope access to |current_texture_|
  // because it requires Begin/EndSharedImageAccessDirectCHROMIUM().
  unsigned current_texture_ = 0u;
  unsigned last_bound_texture_ = 0u;
  gpu::Mailbox last_bound_mailbox_;
  unsigned texture_target_ = 0u;

  unsigned fbo_ = 0u;

  bool use_stencil_ = false;
  unsigned stencil_buffer_ = 0u;

  gfx::OverlayTransform display_transform_ = gfx::OVERLAY_TRANSFORM_NONE;
  gfx::Size reshape_size_;
  gfx::Size swap_size_;
};

}  // namespace viz

#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_GL_OUTPUT_SURFACE_BUFFER_QUEUE_H_
